import numpy as np

from ..element_h1 import ElementH1
from ...refdom import RefTri


class ElementTriP4(ElementH1):
    """Piecewise quartic element."""

    nodal_dofs = 1
    facet_dofs = 3
    interior_dofs = 3
    maxdeg = 4
    dofnames = ["u", "u", "u", "u", "u", "u", "u"]
    doflocs = np.array(
        [
            [0.0, 0.0],
            [1.0, 0.0],
            [0.0, 1.0],
            [1.0 / 4, 0.0],
            [1.0 / 2, 0.0],
            [3.0 / 4, 0.0],
            [3.0 / 4, 1.0 / 4],
            [1.0 / 2, 1.0 / 2],
            [1.0 / 4, 3.0 / 4],
            [0.0, 1.0 / 4],
            [0.0, 1.0 / 2],
            [0.0, 3.0 / 4],
            [1 / 4.0, 1.0 / 4],
            [1.0 / 2, 1.0 / 4],
            [1.0 / 4, 1.0 / 2],
        ]
    )

    refdom = RefTri

    def lbasis(self, X, i):
        x, y = X

        if i == 0:  # (0., 0.)
            phi = (
                1
                / 6
                * (1 - x - y)
                * (4 * (1 - x - y) - 1)
                * (4 * (1 - x - y) - 2)
                * (4 * (1 - x - y) - 3)
            )
            dphi = np.array(
                [
                    (128 * x ** 3) / 3
                    + 128 * x ** 2 * y
                    - 80 * x ** 2
                    + 128 * x * y ** 2
                    - 160 * x * y
                    + (140 * x) / 3
                    + (128 * y ** 3) / 3
                    - 80 * y ** 2
                    + (140 * y) / 3
                    - 25 / 3,
                    (128 * x ** 3) / 3
                    + 128 * x ** 2 * y
                    - 80 * x ** 2
                    + 128 * x * y ** 2
                    - 160 * x * y
                    + (140 * x) / 3
                    + (128 * y ** 3) / 3
                    - 80 * y ** 2
                    + (140 * y) / 3
                    - 25 / 3,
                ]
            )
        elif i == 1:  # (1., 0.)
            phi = 1 / 6 * x * (4 * x - 1) * (4 * x - 2) * (4 * x - 3)
            dphi = np.array(
                [(128 * x ** 3) / 3 - 48 * x ** 2 + (44 * x) / 3 - 1, 0.0 * x]
            )
        elif i == 2:  # (0., 1.)
            phi = 1 / 6 * y * (4 * y - 1) * (4 * y - 2) * (4 * y - 3)
            dphi = np.array(
                [0.0 * x, (128 * y ** 3) / 3 - 48 * y ** 2 + (44 * y) / 3 - 1]
            )
        elif i == 3:  # 0->1: (1/4,0)
            phi = (
                8
                / 3
                * x
                * (1 - x - y)
                * (4 * (1 - x - y) - 1)
                * (4 * (1 - x - y) - 2)
            )
            dphi = np.array(
                [
                    -(512 * x ** 3) / 3
                    - 384 * x ** 2 * y
                    + 288 * x ** 2
                    - 256 * x * y ** 2
                    + 384 * x * y
                    - (416 * x) / 3
                    - (128 * y ** 3) / 3
                    + 96 * y ** 2
                    - (208 * y) / 3
                    + 16,
                    -(
                        16
                        * x
                        * (
                            24 * x ** 2
                            + 48 * x * y
                            - 36 * x
                            + 24 * y ** 2
                            - 36 * y
                            + 13
                        )
                    )
                    / 3,
                ]
            )
        elif i == 4:  # 0->1: (1/2,0)
            phi = 4 * x * (1 - x - y) * (4 * x - 1) * (4 * (1 - x - y) - 1)
            dphi = np.array(
                [
                    256 * x ** 3
                    + 384 * x ** 2 * y
                    - 384 * x ** 2
                    + 128 * x * y ** 2
                    - 288 * x * y
                    + 152 * x
                    - 16 * y ** 2
                    + 28 * y
                    - 12,
                    4 * x * (4 * x - 1) * (8 * x + 8 * y - 7),
                ]
            )
        elif i == 5:  # 0->1: (3/4,0)
            phi = 8 / 3 * x * (1 - x - y) * (4 * x - 1) * (4 * x - 2)
            dphi = np.array(
                [
                    64 * x * y
                    - (16 * y) / 3
                    - (224 * x) / 3
                    - 128 * x ** 2 * y
                    + 224 * x ** 2
                    - (512 * x ** 3) / 3
                    + 16 / 3,
                    -(8 * x * (4 * x - 1) * (4 * x - 2)) / 3,
                ]
            )
        elif i == 6:  # 1->2: (3/4,1/4)
            phi = 8 / 3 * x * y * (4 * x - 1) * (4 * x - 2)
            dphi = np.array(
                [
                    (16 * y * (24 * x ** 2 - 12 * x + 1)) / 3,
                    (8 * x * (4 * x - 1) * (4 * x - 2)) / 3,
                ]
            )
        elif i == 7:  # 1->2: (1/2,1/2)
            phi = 4 * x * y * (4 * x - 1) * (4 * y - 1)
            dphi = np.array(
                [
                    4 * y * (8 * x - 1) * (4 * y - 1),
                    4 * x * (4 * x - 1) * (8 * y - 1),
                ]
            )
        elif i == 8:  # 1->2: (1/4,3/4)
            phi = 8 / 3 * x * y * (4 * y - 1) * (4 * y - 2)
            dphi = np.array(
                [
                    (8 * y * (4 * y - 1) * (4 * y - 2)) / 3,
                    (16 * x * (24 * y ** 2 - 12 * y + 1)) / 3,
                ]
            )
        elif i == 9:  # 0->1: (0,1/4)
            phi = (
                8
                / 3
                * y
                * (1 - x - y)
                * (4 * (1 - x - y) - 1)
                * (4 * (1 - x - y) - 2)
            )
            dphi = np.array(
                [
                    -(
                        16
                        * y
                        * (
                            24 * x ** 2
                            + 48 * x * y
                            - 36 * x
                            + 24 * y ** 2
                            - 36 * y
                            + 13
                        )
                    )
                    / 3,
                    -(128 * x ** 3) / 3
                    - 256 * x ** 2 * y
                    + 96 * x ** 2
                    - 384 * x * y ** 2
                    + 384 * x * y
                    - (208 * x) / 3
                    - (512 * y ** 3) / 3
                    + 288 * y ** 2
                    - (416 * y) / 3
                    + 16,
                ]
            )
        elif i == 10:  # 0->2: (0,1/2)
            phi = 4 * y * (1 - x - y) * (4 * y - 1) * (4 * (1 - x - y) - 1)
            dphi = np.array(
                [
                    4 * y * (4 * y - 1) * (8 * x + 8 * y - 7),
                    128 * x ** 2 * y
                    - 16 * x ** 2
                    + 384 * x * y ** 2
                    - 288 * x * y
                    + 28 * x
                    + 256 * y ** 3
                    - 384 * y ** 2
                    + 152 * y
                    - 12,
                ]
            )
        elif i == 11:  # 0->2: (0,3/4)
            phi = 8 / 3 * y * (1 - x - y) * (4 * y - 1) * (4 * y - 2)
            dphi = np.array(
                [
                    -(8 * y * (4 * y - 1) * (4 * y - 2)) / 3,
                    64 * x * y
                    - (224 * y) / 3
                    - (16 * x) / 3
                    - 128 * x * y ** 2
                    + 224 * y ** 2
                    - (512 * y ** 3) / 3
                    + 16 / 3,
                ]
            )
        elif i == 12:  # (1/4,1/4)
            phi = 32 * x * y * (1 - x - y) * (4 * (1 - x - y) - 1)
            dphi = np.array(
                [
                    32
                    * y
                    * (
                        12 * x ** 2
                        + 16 * x * y
                        - 14 * x
                        + 4 * y ** 2
                        - 7 * y
                        + 3
                    ),
                    32
                    * x
                    * (
                        4 * x ** 2
                        + 16 * x * y
                        - 7 * x
                        + 12 * y ** 2
                        - 14 * y
                        + 3
                    ),
                ]
            )
        elif i == 13:  # (1/2,1/4)
            phi = 32 * x * y * (1 - x - y) * (4 * x - 1)
            dphi = np.array(
                [
                    -32 * y * (8 * x * y - y - 10 * x + 12 * x ** 2 + 1),
                    -32 * x * (4 * x - 1) * (x + 2 * y - 1),
                ]
            )
        elif i == 14:  # (1/4,1/2)
            phi = 32 * x * y * (1 - x - y) * (4 * y - 1)
            dphi = np.array(
                [
                    -32 * y * (4 * y - 1) * (2 * x + y - 1),
                    -32 * x * (8 * x * y - 10 * y - x + 12 * y ** 2 + 1),
                ]
            )
        else:
            self._index_error()

        return phi, dphi
